#pragma once
#include <map>
#include<memory>

namespace Hi {
// Max RTP length.
constexpr size_t RTP_BUFFER_SIZE{65536};
// Max MTU size.
//constexpr size_t MTU_SIZE{1500};

struct PayloadDescriptor;

class PayloadDescriptorHandler
{
public:
  virtual PayloadDescriptor *payloadDescriptor() const = 0;
};

class RtpPacket
{
public:
  /* Struct for RTP header. */
  struct Header
  {
#if defined(MS_LITTLE_ENDIAN)
    uint8_t csrcCount : 4;	
    uint8_t extension : 1;	
    uint8_t padding : 1;	
    uint8_t version : 2;
    uint8_t payloadType : 7;
    uint8_t marker : 1;
#elif defined(MS_BIG_ENDIAN)
    uint8_t version : 2;	// RTP版本， RFC3550中规定版本号为2
    uint8_t padding : 1;	// 如果被置位，则在packet末尾包含了一个额外的附加信息。附加信息的最后一个字节标识额外附加信息的长度.
							//      该字段之所有存在是因为一些加密机制需要固定长度的数据块，或者为了在一个底层协议数据单元中传输多个RTP Packets
    uint8_t extension : 1;	// 如果被置位，固定头后面紧跟一个头的扩展
							//     如果RTP头后的第一个16位固定位0xBEDE， 意味着这是一个one-byte扩展
    uint8_t csrcCount : 4;	// 固定有后面的CSRC标识符的数目， 这个域只有在通过一个混合器才有非0值
							//    csrc： 特约信源标识符，每个占32位。 每个csrc标识了包含在该rtp报文有效载荷中的所有特约信源
							//    同步信源： 产生流媒体的信源，eg：麦克风，相机，rtp混合器等 
    uint8_t marker : 1;		// 该位的功能依赖于profile的定义。 profile可以改变该位的长度，但要保持marker和payload type总长度不变（共8bit）
    uint8_t payloadType : 7;// 携带信息的类型。如果接收方不能识别，则忽略该packet	
#endif
    uint16_t sequenceNumber;// 序列号，每个packet发送后，该序列号加1， 接收方可以饭局该序列号重新排序
    uint32_t timestamp;		// 时间戳，反应packet所携带信息包中第一个字节的采样时间
    uint32_t ssrc;			// 标识数据源。 在一个RTP Session间每个数据流应该有一个不同的SSRC。
  };

public:
  /* Struct for RTP extension header. */
  struct ExtensionHeader
  {
    uint16_t id;		// 如果RTP头后的第一个16位固定位0xBEDE， 意味着这是一个one-byte扩展
    uint16_t length; 	// Size of value in multiples of 4 bytes.
    uint8_t value[1];
  };

private:
  /* Struct for One-Byte extension. */
  // 在RTP头后面紧跟着的是0xBEDE， 则意味着是one-byte扩展
  //  在0xBEDE后面4位， 是扩展头的个数，然后就是OneByteExtension
  //   One-Byte extension 可能会有多个， 与0xBEDE后面4位的数量相同
  struct OneByteExtension
  {
#if defined(MS_LITTLE_ENDIAN)
    uint8_t len : 4;
    uint8_t id : 4;
#elif defined(MS_BIG_ENDIAN)
    uint8_t id : 4;
    uint8_t len : 4;
#endif
    uint8_t value[1];
  };

private:
  /* Struct for Two-Bytes extension. */
  // 在RTP头后面以0x100(0b00010000)开头，则是Two-Bytes.
  struct TwoBytesExtension
  {
#if defined(MS_LITTLE_ENDIAN)
    uint8_t len : 8;
    uint8_t id : 8;
#elif defined(MS_BIG_ENDIAN)
    uint8_t id : 8;
    uint8_t len : 8;
#endif
    uint8_t value[1];
  };

public:
  RtpPacket(Header *header, ExtensionHeader *extensionHeader,
            const uint8_t *payload, size_t payloadLength,
            uint8_t payloadPadding, size_t size);
  ~RtpPacket();
  bool ParsePadding();
  const Header *GetHeader() const;
  const uint8_t *GetData() const;
  size_t GetSize() const;
  uint8_t GetPayloadType() const;
  uint16_t GetSequenceNumber() const;
  void SetSequenceNumber(uint16_t seq);
  uint32_t GetTimestamp() const;
  void SetTimestamp(uint32_t timestamp);
  uint32_t GetSsrc() const;
  void SetSsrc(uint32_t ssrc);
  bool HasExtensionHeader() const;
  const ExtensionHeader *GetExtensionHeader() const;
  uint16_t GetExtensionHeaderId() const;
  size_t GetExtensionHeaderLength() const;
  uint8_t *GetPayload() const;
  size_t GetPayloadLength() const;
  bool IsKeyFrame() const;
  RtpPacket *Clone(const uint8_t *buffer) const;  
  RtpPacket *Clone(const uint8_t *buffer, const std::function<void(uint8_t*)>& releaseFun) const;  
  
  void SetPayloadDescriptorHandler(PayloadDescriptorHandler *payloadDescriptorHandler);
  PayloadDescriptor *payloadDescriptor() const;
  bool GetMark() const;
public:
	void SetUserRelease(const std::function<void(uint8_t*)>& releaseFun);
private:
	bool HasOneByteExtensions() const;
	bool HasTwoBytesExtensions() const;

	void ParseExtensions();
	void ParseExtensions_HasOneByteExtension();
	void ParseExtensions_HasTwoByteExtensions();
private:
  // Passed by argument.
  Header *header{nullptr};
  uint8_t *csrcList{nullptr};
  ExtensionHeader *extensionHeader{nullptr};
  std::map<uint8_t, OneByteExtension *> oneByteExtensions;
  std::map<uint8_t, TwoBytesExtension *> twoBytesExtensions;
  uint8_t *payload{nullptr};
  size_t payloadLength{0};
  uint8_t payloadPadding{0};
  size_t size{0}; // Full size of the packet in bytes.
  
  std::shared_ptr<PayloadDescriptorHandler> payloadDescriptorHandler;
private:
	bool usingUserReleaseFun{false};
	std::function<void(uint8_t*)> releaseFunc;
	uint8_t *satelliteMemory{nullptr};
};
}